﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using CI.Interfaces;
using CI.Misc;
using CI.Organism;
using CI.PW.Controls;
using CI.PW.ViewModels;

namespace CI.PW.Models
{
    public class VertexModel : DataModel, IDisposable
    {
        public bool IsNeuron { get; private set; }
        public bool IsSynapse { get { return !IsNeuron; } }
        public long ThresholdValue { get; private set; }

        public ICommand ShowExtendedData { get; private set; }
        private Window ExtendedDataWindow;


        private readonly PropertyChangedEventArgs _currentPropertyChanged = new PropertyChangedEventArgs("CurrentValue");
        private long _currentValue;
        public long CurrentValue
        {
            get { return _currentValue; }
            private set
            {
                if (_currentValue == value)
                    return;

                _currentValue = value;
                RaisePropertyChanged(_currentPropertyChanged);
            }
        }

        private readonly PropertyChangedEventArgs _synapsePropertyChanged = new PropertyChangedEventArgs("SynapseValue");
        private long _synapseValue;
        public long SynapseValue
        {
            get { return _synapseValue; }
            private set
            {
                if (_synapseValue == value)
                    return;
                _synapseValue = value;
                RaisePropertyChanged(_synapsePropertyChanged);
            }
        }

        private readonly PropertyChangedEventArgs _lastEvalPropertyChanged = new PropertyChangedEventArgs("LastEvaluationValue");
        private long _lastEvaluationValue;
        public long LastEvaluationValue
        {
            get { return _lastEvaluationValue; }
            private set
            {
                if (_lastEvaluationValue == value)
                    return;

                _lastEvaluationValue = value;
                RaisePropertyChanged(_lastEvalPropertyChanged);
            }
        }

        private readonly PropertyChangedEventArgs _lastActPropertyChanged = new PropertyChangedEventArgs("LastActivationValue");
        private long _lastActivationValue;
        public long LastActivationValue
        {
            get { return _lastActivationValue; }
            private set
            {
                if (_lastActivationValue == value)
                    return;

                _lastActivationValue = value;

                RaisePropertyChanged(_lastActPropertyChanged);
            }
        }

        public readonly SynapseIO SynapseIO;
        public readonly INeuron Neuron;

        private readonly INeuralSystem _neuralSystem;

        public event Action<VertexModel> VertexActivation;


        public VertexModel(SynapseIO synapseIO, INeuralSystem neuralSystem)
        {
            IsNeuron = false;
            SynapseIO = synapseIO;
            _neuralSystem = neuralSystem;
            SynapseIO.ActivationEvent += SynapseActivationEvent;

            if (_neuralSystem != null)
            {
                _neuralSystem.NotifyEvaluationStop += SynapseNeuralSystemNotifyEvaluation;
            }

            // todo : move to base constructor for all vertex model types to open windows showing more info?
            if (synapseIO.BindingName == Globals.CYCLOPS)
            {
                ShowExtendedData = new DelegateCommand(ShowCyclops);
            }

        }

        private void ShowCyclops()
        {
            var pixelViewModel = new PixelViewerViewModel((VisualSynapseIO)SynapseIO);
            ExtendedDataWindow = new PixelViewer(pixelViewModel);
            ExtendedDataWindow.Show();
        }

        void SynapseNeuralSystemNotifyEvaluation()
        {
            SynapseValue = SynapseIO.CurrentDisplayValue;
        }

        public VertexModel(INeuron neuron)
        {
            IsNeuron = true;
            Neuron = neuron;
            ThresholdValue = neuron.ThresholdValue;
            neuron.ActivationFunctionComplete += NeuronActivationFunctionComplete;
            neuron.NeuralSystem.NotifyEvaluationStop += NeuronNeuralSystemNotifyEvaluationStop;
            neuron.ActivationEvent += NeuronActivationEvent;
        }



        void RaiseVertexActivation()
        {
            if (VertexActivation != null)
                VertexActivation(this);
        }

        void NeuronActivationEvent(long value)
        {
            LastActivationValue = Neuron.SummedInputValue;
            RaiseVertexActivation();
        }

        void SynapseActivationEvent(long value)
        {
            RaiseVertexActivation();
        }

        void NeuronNeuralSystemNotifyEvaluationStop()
        {
            CurrentValue = Neuron.SummedInputValue;
        }

        void NeuronActivationFunctionComplete()
        {
            LastEvaluationValue = Neuron.SummedInputValue;
        }

        public void Dispose()
        {
            DisposeExtendedDataWindow();
            DisposeNeuron();
            DisposeSynapseIO();
        }

        private void DisposeSynapseIO()
        {
            if (SynapseIO != null)
            {
                _neuralSystem.NotifyEvaluationStart -= SynapseNeuralSystemNotifyEvaluation;
                _neuralSystem.NotifyEvaluationStop -= SynapseNeuralSystemNotifyEvaluation;
                SynapseIO.ActivationEvent -= SynapseActivationEvent;
            }
        }

        private void DisposeNeuron()
        {
            if (Neuron != null)
            {
                Neuron.ActivationFunctionComplete -= NeuronActivationFunctionComplete;
                Neuron.NeuralSystem.NotifyEvaluationStop -= NeuronNeuralSystemNotifyEvaluationStop;
                Neuron.ActivationEvent -= NeuronActivationEvent;
            }
        }

        private void DisposeExtendedDataWindow()
        {
            if (ExtendedDataWindow != null)
                ExtendedDataWindow.Close();
        }

        public override string ToString()
        {
            return Neuron != null ? Neuron.ToString() : SynapseIO.ToString();
        }
    }
}